package top.fangwenmagician.calculationstring;

import top.fangwenmagician.calculationstring.log.I_CalculationLog;
import top.fangwenmagician.calculationstring.operator.I_Operator;

import java.util.List;

/**
 * <h1>用于计算字符串的对象。</h1>
 * 该对象应在程序开始时就创建完毕，而不是每次运算都从工厂里创建一个。<br />
 * 在计算过程中，优先级（{@link I_Operator#priority()}）高的运算符会率先运行。<br />
 * 在相同优先级的运算符中，会根据同级优先度（{@link I_Operator#peerPriority(String)}）依次进行运算。<br /><br />
 * <h2>计算字符串</h2>
 * 您可以通过使用对象内的{@link CalculationString#calculation(String)}函数来计算字符串。<br />
 * 或者通过创建基于{@link I_CalculationLog}接口的日志类，
 * 使用{@link CalculationString#calculation(String, I_CalculationLog)}函数来获取个性化的日志对象。<br /><br />
 * <h2>获取和更改配置对象</h2>
 * {@link CalculationString}实现了{@link I_CalculationConfigure}接口。<br />
 * 可以通过{@link CalculationString#getConfigure()}或{@link CalculationString#getConfigure(Class)}来获取配置。<br /><br />
 * <h2>线程安全性</h2>
 * 在绝大部分情况下，该对象是<b>线程安全</b>的。<br />
 * 在下列情况下，线程不安全：<br />
 * <ul>
 *     <li>配置并使用了了线程不安全的{@link CalculationString#configure}对象</li>
 *     <li>使用了线程不安全的运算符（本框架默认提供的运算符均为线程安全的）</li>
 * </ul>
 */
public class CalculationString implements I_CalculationString, I_CalculationConfigure {
    /**
     * 排序后的运算符组列表，优先级高的在前面
     */
    private final List<OperatorGroup> operatorGroupList;
    /**
     * 自定义配置
     */
    private final Object configure;

    /**
     * 内部构造函数，{@link CalculationString}对象只能通过{@link CalculationFactory}对象创建
     *
     * @param operatorGroupList 运算符组列表
     */
    CalculationString(List<OperatorGroup> operatorGroupList,Object configure) {
        this.operatorGroupList = operatorGroupList;
        this.configure=configure;
    }

    /**
     * 计算表达式
     *
     * @param expression 表达式
     * @return 结果
     * @throws Exception 计算中抛出的异常
     */
    @Override
    public String calculation(String expression) throws Exception {
        return calculation(expression, null);
    }

    /**
     * 计算表达式，并返回计算日志
     *
     * @param expression 表达式
     * @param log        日志对象，需要继承{@link I_CalculationLog}接口
     * @param <T>        需要继承{@link I_CalculationLog}接口
     * @return 结果
     * @throws Exception 计算中抛出的异常
     */
    @Override
    public <T extends I_CalculationLog> String calculation(String expression, T log) throws Exception {
        for (OperatorGroup operatorGroup : operatorGroupList) {
            expression = operatorGroup.calculation(expression, log);
        }
        return expression;
    }

    @Override
    public Object getConfigure() {
        return configure;
    }

    @Override
    public <T> T getConfigure(Class<T> tClass) {
        return tClass.cast(configure);
    }
}
